package com.haiqiu.file.local.utils;


import com.haiqiu.file.constant.Constants;
import com.haiqiu.file.local.config.LocalConfig;
import com.haiqiu.tools.common.utils.HttpServletUtils;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 服务器文件上传
 *
 * @author HaiQiu
 */

public class ServerFileUtils {

    private static final Logger log = LoggerFactory.getLogger(ServerFileUtils.class);


    /**
     * 务器映射的地址+端口
     */
    private static String server = "http://localhost:8080";

    /**
     * 文件父级存储路径
     */
    private static String parent = "C:\\";

    /**
     * 文件资源虚拟映射路径
     */
    private static String virtual = "/resource";

    private static final String DELETE_LINUX_PATH = "/";
    private static final String DELETE_WINDOWS_PATH = "C:\\";

    public static void init(LocalConfig config) {
        server = config.getServer();
        parent = config.getParent();
        virtual = config.getVirtual();
    }


    /**
     * 本地文件上传
     *
     * @param file   文件
     * @param folder 上传目标目录
     * @return
     */
    public static String upload(String folder, MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new RuntimeException("未选择文件");
        }
        //获取原始文件名称
        String originalFilename = file.getOriginalFilename();
        //获取文件名前缀，如源名为123.txt，获取得到123
        String prefix = originalFilename.substring(0, originalFilename.lastIndexOf("."));
        //获取文件后缀名
        //.jpg
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));

        //获取资源路径
        String path = null;
        if (StringUtils.hasText(folder)) {
            if (parent.equals(DELETE_LINUX_PATH)) {
                path = folder;
            } else if (!folder.contains(parent)) {
                path = parent + folder;
            } else {
                path = folder;
            }
        } else {
            path = parent;
        }

        log.info("***********************上传目标路径**************************\n\n\n");
        log.info(path + "\n\n\n");
        log.info("***********************上传目标路径**************************\n\n\n");

        //全路径存放在文件类中，判断文件夹是否存在不存在就创建
        //也可以直接放进去进行拼接 File dataFile = new File(path,format);
        File FilePath = new File(path);
        if (!FilePath.exists()) {
            FilePath.mkdirs();
        }

        //文件存在，就以(1)的方式叠加上去
        String fileName = createFileName(prefix, suffix, path);

        //文件上传至指定路径
        try {
            file.transferTo(new File(FilePath, fileName));
            log.info("上传成功");

            String url = server + virtual + path.replace(parent, "") + "/" + fileName;
            return url;

        } catch (IOException e) {
            log.error("上传失败");
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 文件存在就叠加+1
     *
     * @param prefix 文件名前缀，不包含.
     * @param suffix 文件名后缀 .txt
     * @param path   上级目录
     * @return 返回新的文件名
     */
    public static String createFileName(String prefix, String suffix, String path) {
        File file = new File(path, prefix + suffix);
        int i = 1;
        while (file.exists()) {
            file = new File(path, prefix + "(" + i + ")" + suffix);
            i++;
        }
        if (i != 1) {
            i = i - 1;
            return prefix + "(" + i + ")" + suffix;
        } else {
            return prefix + suffix;
        }

    }

    
    public static void download(String url) {
        if (!url.startsWith(server)) {
            throw new RuntimeException("文件地址错误");
        }
        String[] splitUrl = url.split(server + virtual);
        String path = splitUrl[splitUrl.length - 1];
        //组装父级目录,得到文件完整路径
        String filePath = parent + path;
        ServletOutputStream out = null;
        FileInputStream in = null;
        HttpServletResponse response = HttpServletUtils.getServletResponse();
        try {
            in = new FileInputStream(new File(filePath));
            String[] dir = path.split("/");
            String fileName = dir[dir.length - 1];
            fileName = URLEncoder.encode(fileName, "UTF-8");
//            fileName = new String(fileName.getBytes("UTF-8"), "ISO8859-1");
            // 自定义的header
            response.setHeader("requestType", "file");
            // 自定义的header
            response.setHeader("requestFileName", fileName);
            //设置这个header 可见
            response.setHeader("Access-Control-Expose-Headers", "requestType,requestFileName");

//             设置响应类型为html，编码为utf-8，处理相应页面文本显示的乱码
            response.setContentType("application/octet-stream;charset=UTF-8");
//            // 设置文件头：最后一个参数是设置下载文件名
            response.setHeader("Content-disposition", "attachment;fileName=" + fileName);
            out = response.getOutputStream();
            // 读取文件流
            int len = 0;
            byte[] buffer = new byte[1024 * 10];
            while ((len = in.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
            out.flush();
        } catch (IOException e) {
            throw new RuntimeException("文件下载失败");
        }

    }

    public static boolean delete(String url) {
        if (!url.startsWith(server)) {
            throw new RuntimeException("文件地址错误");
        }
        String[] splitUrl = url.split(server);
        String path = splitUrl[splitUrl.length - 1];
        //组装父级目录,得到文件完整路径
        String filePath = parent + path;
        return deleteFile(filePath);
    }


    public static boolean deleteFile(String filePath) {// 删除单个文件
        File file = new File(filePath);
        if (file.exists() && file.isFile()) {
            file.delete();// 文件删除
            return true;
        }
        return true;
    }

    /**
     * 根据路径删除指定目录或文件，无论存在与否
     *
     * @param deletePath 指定目录或文件
     * @return 是否成功
     */
    public static boolean DeleteFolder(String deletePath) {
        File file = new File(deletePath);
        if (!file.exists()) {
            return false;
        } else {
            if (file.isFile()) { // 判断是否为文件
                return deleteFile(deletePath); // 是文件时调用删除文件方法
            } else {
                return deleteDirectory(deletePath);// 目录时调用删除目录的方法
            }
        }
    }

    // 删除目录(文件夹)以及目录下的文件
    public static boolean deleteDirectory(String dirPath) {
        boolean flag = false;
        // 如果dirPath不以文件分隔符结尾，自动添加文件分隔符
        if (!dirPath.endsWith(File.separator)) {
            dirPath += File.separator;
        }
        File dirFile = new File(dirPath);
        if (!dirFile.exists() || !dirFile.isDirectory()) {
            return false;
        }
        flag = true;
        // 下面该句代码是程序的核心：获得传入路径下的所有文件
        File[] files = dirFile.listFiles();// 获得传入路径下的所有文件
        // 遍历删除文件夹下的所有文件(包括子目录)
        for (int i = 0; i < files.length; i++) {
            if (files[i].isFile()) {// 删除子文件
                flag = deleteFile(files[i].getAbsolutePath());
                log.info(files[i].getAbsolutePath() + " 删除成功");
                if (!flag) {
                    break; // 如果删除失败则跳出
                }
            } else {// 运用递归，删除子目录
                flag = deleteDirectory(files[i].getAbsolutePath());
                if (!flag) {
                    break;// 如果删除失败则跳出
                }
            }

        }
        if (!flag) {
            return false;
        }
        // 删除当前目录
        if (dirFile.delete()) {
            return true;
        } else {
            return false;
        }
    }

    public static List<FileServer> getFiles(String path, String name, String sort) {
        if (StringUtils.hasText(path)){
            if (path.startsWith(virtual)){
                path = path.replace(virtual,"");
            }
            if (parent.endsWith(Constants.CARVE) && path.startsWith(Constants.CARVE)){
                path  = path.replace(Constants.CARVE,"");
            }
            if (parent.endsWith(Constants.CARVE_WIN) && path.startsWith(Constants.CARVE_WIN)){
                path  = path.replace(Constants.CARVE_WIN,"");
            }
            path = parent + path;
        }else {
            path = parent;
        }
        List<FileServer> fileServerList = new LinkedList<>();
        File file = new File(path);
        //目录不存在就创建
        if (!file.exists()) {
            file.mkdirs();
        }
        // 如果这个路径是文件夹
        if (file.isDirectory()) {
            // 获取路径下的所有文件
            getDir(fileServerList, file);

            //否则即是文件
        } else if (file.isFile()) {
            //设置file信息
            FileServer fileServer = setServerFile(file);
            setBack(file, fileServer);
            fileServerList.add(fileServer);
        }
        //筛选模糊匹配字段
        if (name != null) {
            fileServerList = fileServerList.stream().filter(item -> item.getFilename().contains(name)).collect(Collectors.toList());
        }
        // 排序(隐藏文件先进行排序，再次进行文件夹排序)
        fileServerList.sort(Comparator.comparing(FileServer::getHidden).reversed());
        fileServerList.sort(Comparator.comparing(FileServer::getDirectory).reversed());
        if (!StringUtils.isEmpty(sort)) {
            switch (sort) {
                case Constants.SIZE_ASC:
                    fileServerList.sort(Comparator.comparing(FileServer::getSize));
                    break;
                case Constants.SIZE_DESC:
                    fileServerList.sort(Comparator.comparing(FileServer::getSize).reversed());
                    break;
                case Constants.DATE_ASC:
                    fileServerList.sort(Comparator.comparing(FileServer::getLastModifiedTime));
                    break;
                case Constants.DATE_DESC:
                    fileServerList.sort(Comparator.comparing(FileServer::getLastModifiedTime).reversed());
                    break;
                default:
                    break;
            }
        }

        // 文件地址
        for (FileServer fileServer : fileServerList) {
            if (fileServer.getUrl() != null) {

                log.info("++++++++++++++++++++获取到的文件url地址++++++++++++++++++++");
                log.info(fileServer.getUrl());
                log.info("++++++++++++++++++++获取到的文件url地址++++++++++++++++++++");

                if (getSystemOs().contains(Constants.WINDOWS)) {
                    String s = fileServer.getUrl();
                    if (fileServer.getUrl().contains(parent)) {
                        s = fileServer.getUrl().replace(parent, "/");
                    }
                    fileServer.setUrl(s.replace("\\", "/"));

                    log.info("++++++++++++++++++++转换windows的地址++++++++++++++++++++");
                    log.info(fileServer.getUrl());
                    log.info("++++++++++++++++++++转换windows的地址++++++++++++++++++++");

                } else {
                    if (fileServer.getUrl().contains(parent)) {
                        fileServer.setUrl(fileServer.getUrl().replace(parent, ""));
                    }

                    log.info("++++++++++++++++++++转换linux地址++++++++++++++++++++");
                    log.info(fileServer.getUrl());
                    log.info("++++++++++++++++++++转换linux地址++++++++++++++++++++");
                }
            }
        }
        return fileServerList;
    }

    public static String getSystemOs() {
        //系统属性
        Properties props = System.getProperties();
        log.info("操作系统的名称：" + props.getProperty("os.name"));
        return props.getProperty("os.name");
    }

    /**
     * 如果是文件夹
     *
     * @param fileServerList 构建文件列表
     * @param file           当前文件对象
     */
    private static void getDir(List<FileServer> fileServerList, File file) {
        File[] files = file.listFiles();
        //如果文件长度为0，则是空文件夹
        if (files == null || files.length == 0) {
            FileServer fileServer = new FileServer();
            fileServer.setFolderIsNull(Constants.ON);
            fileServer.setBack(file.getParent());
            fileServer.setAbsolutePath(file.getAbsolutePath());
            fileServer.setParent(file.getAbsolutePath());
            fileServerList.add(fileServer);
            //否则是文件夹
        } else {
            for (int i = 0; i < files.length; i++) {
                //设置file信息
                FileServer fileServer = setServerFile(files[i]);
                setBack(files[i], fileServer);
                fileServerList.add(fileServer);
            }
        }
    }

    /**
     * 获取上级目录
     *
     * @param file       文件对象
     * @param fileServer 返回自定义文件对象
     */
    public static void setBack(File file, FileServer fileServer) {
        String parentPath = file.getParent();
        File fileParent = new File(parentPath);
        if (parentPath.equals(parent)) {
            fileServer.setBack(parent);
        } else {
            fileServer.setBack(fileParent.getParent());
        }
    }

    private static FileServer setServerFile(File file) {
        FileServer fileServer = new FileServer();
        fileServer.setFilename(file.getName());
        fileServer.setCanRead(file.canRead());
        fileServer.setCanWrite(file.canWrite());
        fileServer.setAbsolutePath(file.getAbsolutePath());
        fileServer.setDirectory(file.isDirectory());
        fileServer.setFilename(file.getName());
        fileServer.setFilename(file.getName());
        fileServer.setFile(file.isFile());
        fileServer.setParent(file.getParent());
        fileServer.setHidden(file.isHidden());
        fileServer.setSize(FileUtils.byteCountToDisplaySize(file.length()));
        fileServer.setLastModifiedTime(new SimpleDateFormat(Constants.TIME_YMDHMS).format(file.lastModified()));
        fileServer.setFolderIsNull(Constants.OFF);
        fileServer.setPath(file.getPath());
        fileServer.setAbsolutePath(file.getAbsolutePath());
        if (file.isFile()) {
            fileServer.setType(file.getName().substring(file.getName().lastIndexOf(".") + 1));
            fileServer.setUrl(server + virtual + file.getPath());
        }
        if (file.isDirectory()) {
            fileServer.setType("文件夹");
        }

        return fileServer;
    }

    public static File[] getFilter(String strFilePath, String strExtension) {

        File[] list = new File(strFilePath).listFiles();

        for (File file : list) {
            if (file.isFile()) {
                if (file.getName().endsWith(strExtension)) {
                    log.info(file.getAbsolutePath());
                }
            }
        }

        return list;
    }


    public static int deleteFiles(Set<String> filePaths) {
        int flag = 0;
        for (String filePath : filePaths) {
            DeleteFolder(filePath);
            flag++;
        }
        return flag;
    }

    public Boolean addFolder(String folders) {
        if (StringUtils.isEmpty(folders)) {
            throw new RuntimeException("文件夹为空");
        }
        //判断是否为程序设定文件夹
        if (folders.contains(parent)) {
            folders = folders.replace(parent, "");
        }
        if ((!folders.startsWith("/")) || !folders.startsWith("\\")) {
            folders = parent + File.separator + folders;
        } else {
            folders = parent + folders;
        }

        File file = new File(folders);
        if (!file.exists()) {
            return file.mkdirs();
        } else {
            throw new RuntimeException("该文件夹已存在");
        }
    }


    public static Boolean serverRename(String filePath, String newName, String parentPath) {
        File file = new File(filePath);
        return file.renameTo(new File(parentPath + File.separator + newName));
    }

    
    public static void copy(String filePath, String newName, String parentPath) {
        InputStream is = null;
        OutputStream os = null;
        try {
            is = Files.newInputStream(Paths.get(filePath));
            os = Files.newOutputStream(new File(parentPath, newName).toPath());
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer)) > 0) {
                os.write(buffer, 0, len);
            }
            is.close();
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }
}
